﻿using LJ.Abstractions.Pack;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LJ.Host.Abstractions.Pack.Internal;


public class PackBootstrapper(IServiceProvider serviceProvider, ILogger<PackBootstrapper> logger) : BackgroundService, IPackBootstrapper
{
    private readonly ILogger<PackBootstrapper> _logger = logger;

    private CancellationTokenSource? _cts;
    private bool _disposed;
    private readonly IEnumerable<IPackProcessingServer> _processors = serviceProvider.GetServices<IPackProcessingServer>();

    public bool IsStarted => !_cts?.IsCancellationRequested ?? false;

    public async Task BootstrapAsync(CancellationToken cancellationToken = default)
    {
        if (_cts != null)
        {
            _logger.LogInformation("### Pack background task is already started!");

            return;
        }

        _logger.LogDebug("### Pack background task is starting.");

        _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);

        _cts.Token.Register(() =>
        {
            _logger.LogDebug("### Pack background task is stopping.");


            foreach (var item in _processors)
                try
                {
                    item.Dispose();
                }
                catch (OperationCanceledException ex)
                {
                    _logger.LogWarning(ex, "Expected an OperationCanceledException, but found '{ex}'.", ex);
                }
        });

        await BootstrapCoreAsync().ConfigureAwait(false);

        _disposed = false;
        _logger.LogInformation("### Pack started!");
    }

    protected virtual async Task BootstrapCoreAsync()
    {
        foreach (var item in _processors)
        {
            try
            {
                _cts!.Token.ThrowIfCancellationRequested();

                await item.StartAsync(_cts!.Token);
            }
            catch (OperationCanceledException)
            {
                // ignore
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Starting the processors throw an exception.");
            }
        }
    }

    public override void Dispose()
    {
        if (_disposed) return;

        _cts?.Cancel();
        _cts?.Dispose();
        _cts = null;
        _disposed = true;
        GC.SuppressFinalize(this);
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await BootstrapAsync(stoppingToken).ConfigureAwait(false);
    }

    public override async Task StopAsync(CancellationToken cancellationToken)
    {
     
        _cts?.Cancel();

        await base.StopAsync(cancellationToken).ConfigureAwait(false);
    }


    public ValueTask DisposeAsync()
    {
        Dispose();
        GC.SuppressFinalize(this);
        return ValueTask.CompletedTask;
    }
}